// PARADIME INTERPRETER
//
//LICENSED FREELY UNDER MIT.

/*
ARRAY CODE taken from AutoIT

	void ArrayDetailsCreate(void);
	int ArrayGetElemIndex(int* subs);
	int DeclareAsArray(int* subs);
	Variant* ArrayGetRef(int* subs, bool bCreate);

Copyright 2006 Jonathon Bennett and the AutoIT Authors

http://www.autoitscript.com/autoit3/docs/credits.htm

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/


#include "stdafx.h"
#include "lexer.h"
#include "variant.h"
#include "tokentypes.h"

//Constructor will not work without a value...?
Variant::Variant(int dummyvalue){
	this->VarType = VAR_INT32; //Default
	this->m_nValue = 0;
	this->AllocatedBytes = 0;
}

Variant::Variant(){
	this->VarType = VAR_INT32; //Default
	this->m_nValue = 0;
	this->AllocatedBytes = 0;
}


int Variant::IsTrue(){
	switch (this->VarType){
		case VAR_INT32:
			if(this->GetInt32())return 1;
			break;
		case VAR_INT64:
			if(this->GetInt64())return 1;
			break;
		case VAR_DOUBLE:
			if(this->GetDouble())return 1;
			break;
		case VAR_STRING:
			if(this->StrRep[0]!='\0')return 1;
			break;
		case VAR_REFERENCE:
			return this->m_pValue->IsTrue();
		case VAR_HWND:
		case VAR_ARRAY:
			return 0;
	}
	return 0;
}

int Variant::GetInt32(){
	switch (this->VarType){
		case VAR_INT32:
			return this->m_nValue;
		case VAR_INT64:
			return (int)this->m_n64Value;
		case VAR_DOUBLE:
			return (int)this->m_fValue;
		case VAR_STRING:
			return atoi(this->StrRep);
		case VAR_REFERENCE:
			return this->m_pValue->GetInt32();
		case VAR_HWND:
		case VAR_ARRAY:
			return 0;
	}

	return 0;
}

__int64 Variant::GetInt64(){
	switch (this->VarType){
		case VAR_INT32:
			return (__int64)this->m_nValue;
		case VAR_INT64:
			return this->m_n64Value;
		case VAR_DOUBLE:
			return (__int64)this->m_fValue;
		case VAR_STRING:
			return _atoi64(this->StrRep);
		case VAR_REFERENCE:
			return this->m_pValue->GetInt64();
		case VAR_HWND:
		case VAR_ARRAY:
			return 0;
	}
	return 0;
}

double Variant::GetDouble(){
	switch (this->VarType){
		case VAR_INT32:
			return (double)this->m_nValue;
		case VAR_INT64:
			return (double)this->m_n64Value;
		case VAR_DOUBLE:
			return this->m_fValue;
		case VAR_STRING:
			return atof(this->StrRep);
		case VAR_REFERENCE:
			return this->m_pValue->GetDouble();
		case VAR_HWND:
		case VAR_ARRAY:
			return 0;
	}
	return 0;
}


char* Variant::GetString(){

	//If a cached version exists
	if(this->AllocatedBytes)return this->StrRep;

	std::ostringstream sTemp;
	sTemp.precision(15);

	switch (this->VarType){
		case VAR_INT32:
			sTemp << this->m_nValue;
			break;
		case VAR_INT64:
			sTemp << this->m_n64Value;
			break;
		case VAR_DOUBLE:
			sTemp << this->m_fValue;			// Have at least 15 digits after the . for precision (default is 6)
			break;
		case VAR_STRING:
			return this->StrRep;
		case VAR_HWND:
			sTemp << this->m_hWnd;
			break;
		case VAR_REFERENCE:
		case VAR_ARRAY:
			return "";
			break;
	}

	this->AllocatedBytes = strlen(sTemp.str().c_str())+1;
	this->StrRep = new char[this->AllocatedBytes];
	strcpy(this->StrRep, sTemp.str().c_str());
	return this->StrRep;
}

void Variant::StorageReset(void){

	//If its a string/cached string, clear+free.
	if(this->AllocatedBytes){
		delete [] this->StrRep;
		this->AllocatedBytes = 0;
	}

	switch (this->VarType){
		case VAR_INT32:
		case VAR_INT64:
		case VAR_DOUBLE:
		case VAR_STRING:
		case VAR_HWND:
		case VAR_REFERENCE:
			break;
		case VAR_ARRAY:
			this->ArrayDeAllocate();
			break;
	}
}

void Variant::SetInt32(int in){
	this->StorageReset();
	this->VarType = VAR_INT32;
	this->m_nValue = in;
}

void Variant::SetFromToken(void* result){
	this->StorageReset();
	token* tok = (token*)result;
	switch(tok->type){
		case OPT_INT32_TOK:
			this->SetInt32(tok->nValue);
			//std::cout << "Assigning to INT32: " << this->m_nValue << '\n';
			return;
			break;
		case OPT_INT64_TOK:
			this->SetInt64(tok->n64Value);
			//std::cout << "Assigning to INT64: " << tok->n64Value << '\n';
			return;
			break;
		case OPT_DOUBLE_TOK:
			this->VarType = VAR_DOUBLE;
			this->m_fValue = tok->fValue;
			break;
		case OPT_STRINGLITERAL_TOK:
			this->VarType = VAR_STRING;
			this->AllocatedBytes = strlen(tok->szValue)+1;
			this->StrRep = new char[this->AllocatedBytes];
			strcpy(this->StrRep, tok->szValue);
			//std::cout << "Assigning to STRING: " << tok->szValue << '\n';
			break;
		case PARSER_VARIABLE:	//Copy the var that is referenced.
			this->VarType = tok->ResolvedVariable->VarType;
			switch(this->VarType){
				case VAR_INT32:
					this->SetInt32(tok->ResolvedVariable->GetInt32());
					break;
				case VAR_INT64:
					this->SetInt64(tok->ResolvedVariable->GetInt64());
					break;
				case VAR_DOUBLE:
					this->SetDouble(tok->ResolvedVariable->GetDouble());
					break;
				case VAR_STRING:
					this->SetString(tok->ResolvedVariable->GetString());
					break;
				case VAR_REFERENCE:
					this->m_pValue = tok->ResolvedVariable->m_pValue;
					break;
			}
	}
}

void Variant::SetInt64(__int64 in){
	this->StorageReset();
	this->VarType = VAR_INT64;
	this->m_n64Value = in;
}

void Variant::SetDouble(double in){
	this->StorageReset();
	this->VarType = VAR_DOUBLE;
	this->m_fValue = in;
}

void Variant::SetString(char* in){
	this->StorageReset();
	this->VarType = VAR_STRING;
	this->AllocatedBytes = strlen(in)+1;
	this->StrRep = new char[this->AllocatedBytes];
	strcpy(this->StrRep, in);
}

////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
//				ARRAY SYSTEM
//Credits to Jon from the AutoIT project. Array code used under provided license.
////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////

void Variant::ArrayDetailsCreate(void)
{
	if (this->VarType != VAR_ARRAY)
	{
		this->m_Array = new VariantArrayDetails;

		this->m_Array->Data			= NULL;
		this->m_Array->nElements		= 0;
		this->m_Array->Dimensions		= 0;
		this->m_Array->DimensionsCur	= 0;

		this->VarType = VAR_ARRAY;
	}

} // ArrayDetailsCreate()

int Variant::DeclareAsArray(int* subs){
	int i;

	this->StorageReset();
	this->ArrayDetailsCreate();

	this->m_Array->Dimensions = subs[0];
	this->m_Array->nElements = 1;

	for (i=0; i<this->m_Array->Dimensions; i++)
	{
		this->m_Array->Subscript[i] = subs[i+1];
		this->m_Array->nElements = this->m_Array->nElements * this->m_Array->Subscript[i];
		// Check if the array is too big (4096*4096 (0x1000000) elements is 64MB JUST FOR THE TABLE!)
		if ( m_Array->nElements >= 0x1000000 )
			return 0;						// Abort
	}
	// Create space for the array (effectively an array of Variant POINTERS)
	this->m_Array->Data = new Variant *[this->m_Array->nElements];

	// We will allocate array entries when required, so just NULL for now
	for (i=0; i<this->m_Array->nElements; i++)
		this->m_Array->Data[i] = NULL;


	return 1;
}	


int Variant::ArrayGetElemIndex(int* subs)
{
	int i, j;
	int index, mult;

	// Do we have array data?
	if (this->VarType != VAR_ARRAY)
		return -1;

	// Convert our multidimensional array to an element in our internal single dimension array
	index = 0;
	for (i=0; i<this->m_Array->Dimensions; i++)
	{
		mult = 1;
		for (j=i+1; j<this->m_Array->Dimensions; j++)
			mult = mult * this->m_Array->Subscript[j];

		mult = mult * subs[i+1];
		index += mult;
	}

	return index;
}



///////////////////////////////////////////////////////////////////////////////
// ArrayGetRef()
//
// Returns a pointer to the current array element
// See GetArrayElem() for mapping info.
//
// By default bCreate is true so the array entry is created.
///////////////////////////////////////////////////////////////////////////////

Variant* Variant::ArrayGetRef(int* subs, bool bCreate)
{
	int	index = this->ArrayGetElemIndex(subs);

	if (index < 0)
	// not a valid element
		return NULL;

	// index is the entry we need to return, if required, allocate the entry
	// otherwise return previously allocated entry

	if (this->m_Array->Data[index] == NULL && bCreate)
		this->m_Array->Data[index] = new Variant();

	return this->m_Array->Data[index];

} // ArrayGetRef()


void Variant::ArrayDeAllocate(void){
	if(this->VarType == VAR_ARRAY){
		int i;

		if(this->m_Array->Data){
			for(i=0;i<this->m_Array->nElements;i++)
			{
				if(this->m_Array->Data[i] != NULL){
					this->m_Array->Data[i]->StorageReset();
					delete this->m_Array->Data[i];
				}
			}
			delete [] this->m_Array->Data;
			delete this->m_Array;
		}

		this->VarType = VAR_INT32;
		this->SetInt32(0);
	}
}


void VariantConversionTest(void){
	std::cout << "VARIANT TYPE CONVERSION TEST:\n\n";
	Variant mate(1);
	
	mate.SetInt32(15234);
	std::cout<<"Variant Test Int32 (should be 15234): "<<mate.GetString()<<' '<<mate.GetInt32()<<' '<<mate.GetInt64()<<'\n';
	mate.SetInt64(8375630);
	std::cout<<"Variant Test Int64 (should be 8375630): "<<mate.GetString()<<' '<<mate.GetInt32()<<' '<<mate.GetInt64()<<'\n';
	mate.SetString("4392871");
	std::cout<<"Variant Test String (should be 4392871): "<<mate.GetString()<<' '<<mate.GetInt32()<<' '<<mate.GetInt64()<<'\n';
	mate.SetString("43928.5674573");
	std::cout<<"Variant Test String (should be 43928.5674573): "<<mate.GetString()<<' '<<mate.GetDouble()<<' '<<mate.GetInt64()<<'\n';
	mate.SetDouble(4521.65743);
	std::cout<<"Variant Test Float (should be 4521.65743): "<<mate.GetString()<<' '<<mate.GetDouble()<<' '<<mate.GetInt64()<<'\n';
	Sleep(8000);
}
